﻿using BlazorAdmin.Generator.DbColumnReaders;
using BlazorAdmin.Generator.Models;
using BlazorAdmin.Generator.Outputs;
using BootstrapBlazor.Components;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace BlazorAdmin.Generator.Generators
{
    public abstract class Generator
    {
        public IOutput output { get; set; }

        public Dictionary<string, string> ParamDic { get; set; } = new();

        public abstract Task<string> RunAsync(GenerateSetting setting, Func<string, Color?, Task> console);

        public string CreateCodeFolders(GenerateSetting setting)
        {
            var pathArr = new List<string>();
            var tmpPath = string.Empty;

            try
            {
                //增加时间搓文件夹，方便打包
                setting.TimePath = DateTime.Now.ToString("yyMMddHHmmss");
                setting.RootFilePath = AppDomain.CurrentDomain.BaseDirectory + $"{setting.TimePath}\\" + setting.RootPath;

                //RootDir 根目录
                if (!Directory.Exists(setting.RootFilePath))
                    Directory.CreateDirectory(setting.RootFilePath.Replace("\\", "/"));

                //EntityDir 实体
                tmpPath = setting.RootFilePath;
                pathArr.AddRange(setting.EntityPath.Split("/", StringSplitOptions.RemoveEmptyEntries));
                pathArr.ForEach(path =>
                {
                    tmpPath += "\\" + path;
                    if (!Directory.Exists(tmpPath))
                        Directory.CreateDirectory(tmpPath.Replace("\\", "/"));
                });
                setting.EntityFilePath = tmpPath;
                pathArr.Clear();

                //InterfaceDir 接口
                tmpPath = setting.RootFilePath;
                pathArr.AddRange(setting.InterfacePath.Split("/", StringSplitOptions.RemoveEmptyEntries));
                pathArr.ForEach(path =>
                {
                    tmpPath += "\\" + path;
                    if (!Directory.Exists(tmpPath))
                        Directory.CreateDirectory(tmpPath.Replace("\\", "/"));
                });
                setting.InterfaceFilePath = tmpPath;
                pathArr.Clear();

                //ServiceDir 服务
                tmpPath = setting.RootFilePath;
                pathArr.AddRange(setting.ServicePath.Split("/", StringSplitOptions.RemoveEmptyEntries));
                pathArr.ForEach(path =>
                {
                    tmpPath += "\\" + path;
                    if (!Directory.Exists(tmpPath))
                        Directory.CreateDirectory(tmpPath.Replace("\\", "/"));
                });
                setting.ServiceFilePath = tmpPath;
                pathArr.Clear();

                //RazorComponentDir 组件
                tmpPath = setting.RootFilePath;
                pathArr.AddRange(setting.RazorComponentPath.Split("/", StringSplitOptions.RemoveEmptyEntries));
                pathArr.ForEach(path =>
                {
                    tmpPath += "\\" + path;
                    if (!Directory.Exists(tmpPath))
                        Directory.CreateDirectory(tmpPath.Replace("\\", "/"));
                });
                setting.RazorComponentFilePath = tmpPath;
                pathArr.Clear();

                //RazorModelDir 模型
                tmpPath = setting.RootFilePath;
                pathArr.AddRange(setting.RazorModelPath.Split("/", StringSplitOptions.RemoveEmptyEntries));
                pathArr.ForEach(path =>
                {
                    tmpPath += "\\" + path;
                    if (!Directory.Exists(tmpPath))
                        Directory.CreateDirectory(tmpPath.Replace("\\", "/"));
                });
                setting.RazorModelFilePath = tmpPath;
                pathArr.Clear();

                //RazorPageDir 页面
                tmpPath = setting.RootFilePath;
                pathArr.AddRange(setting.RazorPagePath.Split("/", StringSplitOptions.RemoveEmptyEntries));
                pathArr.ForEach(path =>
                {
                    tmpPath += "\\" + path;
                    if (!Directory.Exists(tmpPath))
                        Directory.CreateDirectory(tmpPath.Replace("\\", "/"));
                });
                setting.RazorPageFilePath = tmpPath;
                pathArr.Clear();
            }
            catch(Exception ex)
            {
                return ex.ToString();
            }

            return string.Empty;
        }

        public (string Msg, List<Column> Columns) GetTableColumns(GenerateSetting setting)
        {
            DbProviderFactory _factory;

            try
            {
                _factory = DbProviderFactories.GetFactory(setting.ProviderName);
            }
            catch (Exception x)
            {
                var error = x.Message.Replace("\r\n", "\n").Replace("\n", " ");

                return (string.Format("Failed To Load Provider {0} - {1}", setting.ProviderName, error), null);
            }

            try
            {
                using (var conn = _factory.CreateConnection())
                {
                    conn.ConnectionString = setting.ConnectionString ?? setting.DefaultConnectionString;
                    conn.Open();

                    DbColumnReader reader = null;

                    if (_factory.GetType().Name == "MySqlClientFactory")
                    {
                        //reader = new MySqlColumnReader();
                    }
                    else if (_factory.GetType().Name == "SqlClientFactory")
                    {
                        reader = new SqlServerColumnReader();
                    }
                    else if (_factory.GetType().Name == "NpgsqlFactory")
                    {
                        reader = new PostgreColumnReader();
                    }
                    else if (_factory.GetType().Name == "OracleClientFactory")
                    {
                        //reader = new OracleColumnReader();
                    }
                    else
                    {
                        return ($"UnKnow ProviderType {_factory.GetType().Name}", null);
                    }

                    var columns = reader.ReadColumns(conn, _factory, setting.TableName);

                    conn.Close();

                    return (string.Empty, columns);
                }
            }
            catch (Exception x)
            {
                var error = x.Message.Replace("\r\n", "\n").Replace("\n", " ");

                return (string.Format("Failed to read database schema - {0}", error), null);
            }
        }

        public string ToPascalName(string name)
        {
            // 字符串缓冲区
            var sb = new StringBuilder();

            // 如果字符串包含 下划线
            if (name.Contains("_"))
            {
                // 按下划线来切割字符串为数组
                string[] split = name.Split("_", StringSplitOptions.RemoveEmptyEntries);
                // 循环数组操作其中的字符串
                for (int i = 0, index = split.Length; i < index; i++)
                {
                    // 递归调用本方法，添加到字符串缓冲区
                    sb.Append(ToPascalName(split[i]));
                }
            }
            else // 字符串不包含下划线，则转换成字符数组
            {
                char[] ch = name.ToCharArray();
                // 判断首字母是否是小写字母
                if (ch[0] >= 'a' && ch[0] <= 'z')
                {
                    // 利用ASCII码实现大写
                    ch[0] = (char)(ch[0] - 32);
                }
                // 添加进字符串缓存区
                sb.Append(ch);
            }

            // 返回
            return sb.ToString();
        }
    }
}